package com.gugee.common.web.system.product.service;

import cn.hutool.core.date.DateUtil;
import com.gugee.common.web.system.product.dto.OrderResponse;
import com.gugee.common.web.system.product.dto.ProductResponse;
import com.gugee.common.web.system.product.dto.ReportResponse;
import com.gugee.common.web.system.product.repository.ProductRepository;
import com.gugee.common.web.system.product.repository.ReportRecordRepository;
import com.gugee.common.web.system.promise.repository.PermissionLimitRepository;
import com.gugee.common.web.system.promise.repository.PermissionRecordRepository;
import com.gugee.common.web.system.product.model.Product;
import com.gugee.common.web.system.product.model.ProductOrder;
import com.gugee.common.web.system.product.model.ReportRecord;
import com.gugee.common.web.system.product.model.ReportUser;
import com.gugee.common.web.system.product.repository.OrderRepository;
import com.gugee.common.web.system.product.repository.ReportUserRepository;
import com.gugee.common.web.system.promise.repository.PermissionRepository;
import com.gugee.common.web.system.promise.validate.TimeLimit;
import com.gugee.common.web.system.user.model.UserRole;
import com.gugee.common.web.system.user.repository.UserRoleRepository;
import com.xiguaji.boot.toolkit.jdbc.Page;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class ProductService {
    @Autowired
    OrderRepository orderRepository;

    @Autowired
    ProductRepository productRepository;

    @Autowired
    UserRoleRepository userRoleRepository;

    @Autowired
    ProductResponse.ProductMapper productMapper;

    @Autowired
    ReportRecordRepository reportRecordRepository;

    @Autowired
    ReportUserRepository reportUserRepository;

    @Autowired
    TimeLimit timeLimit;

    @Autowired
    PermissionLimitRepository permissionLimitRepository;

    @Autowired
    PermissionRecordRepository permissionRecordRepository;

    @Autowired
    PermissionRepository permissionRepository;

    @Value("${report.starter.expired.day}")
    int reportExpireDay;

    @Value("${report.user.role.starter.expired.day}")
    int reportUserRoleExpireDay;

    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy", Locale.ENGLISH);

    public Page<OrderResponse> getUserOrderPage(long userId, int page, int pageSize, Integer type){
        return orderRepository.getPage(userId,page,pageSize,type);
    }

    public List<ProductResponse> getVersionProductList(long userId){
        val userRoleOptional = userRoleRepository.findTop1ByUserIdAndExpireTimeGreaterThanEqualOrderByRoleIdDesc(userId, new Date());
        val products = productMapper.mapper(productRepository.findByEnableAndTypeOrderByLevel(true,"version"));
        AtomicReference<BigDecimal> deductMoney = new AtomicReference<>(new BigDecimal(0.0));

        products.forEach( p ->{
            p.setHalfYearPrice(getHalfYearPrice(p.getPrice(),1-p.getHalfYearDiscount()));
            p.setYearPrice(getYearPrice(p.getPrice(),1-p.getYearDiscount()));
            p.setDeductPrice(p.getPrice().doubleValue());
            p.setDeductHalfYearPrice(p.getHalfYearPrice());
            p.setDeductYearPrice(p.getYearPrice());
            p.setValidUntil(LocalDateTime.now().plusMonths(1).format(dateTimeFormatter));
            p.setHalfYearValidUntil(LocalDateTime.now().plusMonths(6).format(dateTimeFormatter));
            p.setYearValidUntil(LocalDateTime.now().plusMonths(12).format(dateTimeFormatter));

            if(userRoleOptional.isPresent() && userRoleOptional.get().getRoleId() == p.getId()){
                long expireTime = userRoleOptional.get().getExpireTime().getTime()/1000;
                LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(expireTime,0, ZoneOffset.ofHours(0));
                p.setValidUntil(localDateTime.plusMonths(1).format(dateTimeFormatter));
                p.setHalfYearValidUntil(localDateTime.plusMonths(6).format(dateTimeFormatter));
                p.setYearValidUntil(localDateTime.plusMonths(12).format(dateTimeFormatter));
                deductMoney.set(getDeductMoney(userRoleOptional.get()));
            }
        });

        if(deductMoney.get().doubleValue() > 0){
            for (ProductResponse p : products) {
                if(p.getLevel() == 1 || (userRoleOptional.isPresent() && userRoleOptional.get().getRoleId() == p.getId())){
                    continue;
                }
                p.setDeductPrice(getDeductPrice(p.getPrice().doubleValue(),deductMoney.get()));
                p.setDeductHalfYearPrice(getDeductPrice(p.getHalfYearPrice(),deductMoney.get()));
                p.setDeductYearPrice(getDeductPrice(p.getYearPrice(),deductMoney.get()));
                p.setDeductMoney(deductMoney.get().setScale(2,RoundingMode.HALF_UP).doubleValue());
            }
        }

        return products;
    }

    public List<ProductResponse> getReportProductList(){
        List<ProductResponse> productResponses = productMapper.mapper(productRepository.findByEnableAndTypeOrderByLevel(true,"report"));

        productResponses.forEach( p -> {
            p.setName(p.getNumber()+" Credit");
            p.setDeductPrice(getDiscountPrice(p.getPrice(),1 - p.getDiscount() <= 0 ? 1:1 - p.getDiscount()));
        });

        return productResponses;
    }


    public void confirmProductOrder(String paypalNumber){
        val productOrder = orderRepository.findByPaypalNumber(paypalNumber);
        productOrder.setStatus("1");
        productOrder.setUpdateBy("callback order");

        val product = getProductById(productOrder.getProductId());
        if(product.getType().equals("version")){
            UserRole userRole = userRoleRepository.findByUserIdAndRoleId(productOrder.getUserId(), productOrder.getProductId());
            Date expireTime = new Date();

            if(null == userRole){
                userRole = new UserRole();
                userRole.setUserId(productOrder.getUserId());
                userRole.setRoleId(productOrder.getProductId());
                userRole.setEffectTime(new Date());
                userRole.setCreateBy("callback order");
                userRole.setUpdateBy("callback order");
            }else{
                userRole.setUpdateBy("callback order for renew");

                if(userRole.getExpireTime().getTime() < new Date().getTime()){
                    userRole.setEffectTime(new Date());
                }else{
                    expireTime = userRole.getExpireTime();
                }
            }
            userRole.setTotalAmount(userRole.getTotalAmount().add(productOrder.getProductPrice()).setScale(2));

            LocalDateTime localDateTime = expireTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            userRole.setExpireTime(Date.from(localDateTime.plusMonths(productOrder.getQty()).atZone(ZoneId.systemDefault()).toInstant()));
            userRoleRepository.save(userRole);

        }else if(product.getType().equals("report")){
            ReportUser reportUser;
            val reportUserOptional = reportUserRepository.findByUserId(productOrder.getUserId());
            if(reportUserOptional.isPresent()){
                reportUser = reportUserOptional.get();
                reportUser.setCheckNumber(reportUser.getCheckNumber() + product.getNumber());
                reportUser.setUpdateBy("callback order add");
                reportUser.setUpdateTime(System.currentTimeMillis()/1000);
            }else {
                reportUser = new ReportUser();
                reportUser.setCheckNumber(product.getNumber());
                reportUser.setCreateBy("callback order add");
                reportUser.setCreateTime(System.currentTimeMillis()/1000);
                reportUser.setUpdateBy("callback order add");
                reportUser.setUpdateTime(System.currentTimeMillis()/1000);
            }
            reportUser.setUserId(productOrder.getUserId());
            reportUserRepository.save(reportUser);
        }



        orderRepository.save(productOrder);
    }

    public BigDecimal getDeductMoney(UserRole userRole){
        long totalDay = DateUtil.betweenDay(userRole.getEffectTime(),userRole.getExpireTime(),false);
        BigDecimal dailyCost = userRole.getTotalAmount().divide(new BigDecimal(totalDay), RoundingMode.UP).setScale(2,RoundingMode.HALF_UP);
        long remainingDay = DateUtil.betweenDay(new Date(),userRole.getExpireTime(),false);
        return dailyCost.multiply(new BigDecimal(remainingDay)).setScale(2,RoundingMode.HALF_UP);
    }

    public ProductOrder getProductOrderByPaypalNumber(String orderNumber){
        return orderRepository.findByPaypalNumber(orderNumber);
    }

    // 半年打折价
    public double getHalfYearPrice(BigDecimal price,Double discount){

        return price.multiply(new BigDecimal(discount.toString())).multiply(new BigDecimal(6)).setScale(2).doubleValue();
    }
    // 一年打折价
    public double getYearPrice(BigDecimal price,Double discount){

        return price.multiply(new BigDecimal(discount.toString())).multiply(new BigDecimal(12)).setScale(2).doubleValue();
    }

    // 获取折扣价
    public double getDeductPrice(Double discount , BigDecimal deduct){
        return new BigDecimal(discount.toString()).subtract(deduct).setScale(2,RoundingMode.HALF_UP).doubleValue();
    }

    public double getDiscountPrice(BigDecimal price,double rate){
        return price.multiply(new BigDecimal(rate)).setScale(2,RoundingMode.HALF_UP).doubleValue();
    }



    public void saveOrder(ProductOrder productOrder){
        orderRepository.save(productOrder);
    }

    public Product getProductById(Long id){
        val productOptional = productRepository.findById(id);
        if(productOptional.isPresent()) return productOptional.get();
        return null;
    }


    /**
     * 博主详情是否解锁
     * @param userId
     * @param uid
     * @return
     */
    public boolean isUnlocked(Long userId,long uid){
        boolean unlocked = false;

        if(null != userId){
            val reportRecordOptional = reportRecordRepository.findByUserIdAndUidAndExpireTimeGreaterThan(userId, uid, new Date().getTime()/1000);

            if(reportRecordOptional.isPresent()){
                if(reportRecordOptional.get().getExpireTime() > LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond()){
                    unlocked = true;
                }
            }
        }

        return unlocked;
    }


    /**
     * 根据用户购买的报告查看次数进行解锁操作
     * @param userId
     * @param uid
     * @return
     */
    @Transactional
    public boolean unlockedByReportStarter(long userId,long uid){
        boolean unlocked = false;
        val reportUserOptional = reportUserRepository.findByUserId(userId);

        if(reportUserOptional.isPresent()){
            if (reportUserOptional.get().getCheckNumber() > 0){
                saveReportRecord(userId, uid,reportExpireDay);
                ReportUser reportUser = reportUserOptional.get();
                reportUser.setCheckNumber(reportUser.getCheckNumber()-1);
                reportUser.setUpdateBy("subtract check number");
                reportUserRepository.save(reportUser);
                unlocked = true;
            }
        }
        return unlocked;
    }

    /**
     * 角色权限解锁报告
     * @param userId
     * @param roleId
     * @param uid
     * @return
     */
    public boolean unlockedByUserRole(long userId, long roleId, long uid){
        boolean unlocked = false;
        timeLimit.setPermissionId("BloggerReport");

        if(timeLimit.validate(roleId,userId, TimeLimit.TimeType.DAY, uid)){
            saveReportRecord(userId, uid,3);
            unlocked = true;
        }

        // saveReportRecord(userId, uid);
        return unlocked;
    }

    private void saveReportRecord(long userId, long uid,int reportExpireDay){
        ReportRecord reportRecord = new ReportRecord();
        val reportRecordOptional = reportRecordRepository.findByUserIdAndUidAndExpireTimeGreaterThan(userId, uid, new Date().getTime()/1000);

        if(reportRecordOptional.isPresent()){
            reportRecord = reportRecordOptional.get();
        }else {
            reportRecord.setCreateTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond());
            reportRecord.setCreateBy("create reportRecord");
        }
        reportRecord.setUserId(userId);
        reportRecord.setUid(uid);
        reportRecord.setStartTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond());
        reportRecord.setExpireTime(LocalDateTime.now().plusDays(reportExpireDay).atZone(ZoneId.systemDefault()).toEpochSecond());
        reportRecord.setUpdateTime(LocalDateTime.now().atZone(ZoneId.systemDefault()).toEpochSecond());
        reportRecord.setUpdateBy("create reportRecord from Reports starter");
        reportRecordRepository.save(reportRecord);
    }

    /**
     * 会员剩余报告解锁次数
     * @param userId
     * @param roleId
     * @return
     */
    public int unlockedNumberByRole(long userId,long roleId){
        long permissionId = permissionRepository.findByName("BloggerReport").getId();
        val permissionLimitOptional = permissionLimitRepository.findByPermissionIdAndProductId(permissionId, roleId);
        int totalCount = permissionLimitOptional.get().getLimitCount();
        int recordCount = permissionRecordRepository.countByPermissionLimitIdAndUserIdAndBelong(permissionLimitOptional.get().getId(),
                userId,
                Integer.parseInt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
        return totalCount - recordCount;
    }

    /**
     * 购买解锁报告的次数
     * @param userId
     * @return
     */
    public int unlockedNumberByReportStarter(long userId){
        val reportUserOptional = reportUserRepository.findByUserId(userId);

        if(reportUserOptional.isPresent()){
            return reportUserOptional.get().getCheckNumber();
        }
        return 0;
    }

    /**
     * 未过期的
     * @param userId
     * @return
     */
    public List<ReportResponse> getUnlockReport(long userId){
        return reportRecordRepository.findByUserIdAndExpireTimeGreaterThan(userId,System.currentTimeMillis()/1000);
    }

    /**
     * 已过期的
     * @param userId
     * @return
     */
    public List<ReportResponse> getExpiredReport(long userId){
        return reportRecordRepository.findByUserIdAndExpireTimeLessThan(userId,System.currentTimeMillis()/1000);
    }
}
